#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

typedef struct{	
	ngx_str_t hello_string;
	ngx_int_t hello_counter;
}ngx_http_hello_loc_conf_t;

// actual request handler
static ngx_int_t ngx_http_mydemo_handler(ngx_http_request_t * request);

static ngx_int_t ngx_http_mydemo_ctx_postconfiguration(ngx_conf_t *cf);

static void * ngx_http_mydemo_create_loc_conf(ngx_conf_t *cf);

static char* ngx_http_hello_string_command_handler(ngx_conf_t *cf, ngx_command_t *cmd, void * conf);

static char* ngx_http_hello_counter_command_handler(ngx_conf_t *cf, ngx_command_t *cmd, void * conf);

static ngx_command_t ngx_http_mydemo_commands[] = {
	{
		ngx_string("hello_string"),       // the commmand name
		NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1 | NGX_CONF_NOARGS,
		ngx_http_hello_string_command_handler,			// the command handler
		NGX_HTTP_LOC_CONF_OFFSET,
		offsetof(ngx_http_hello_loc_conf_t, hello_string),
		NULL
	},
	{
		ngx_string("hello_counter"),
		NGX_HTTP_LOC_CONF | NGX_CONF_FLAG,
		ngx_http_hello_counter_command_handler,
		NGX_HTTP_LOC_CONF_OFFSET,
		offsetof(ngx_http_hello_loc_conf_t, hello_counter),
		NULL
	},
	ngx_null_command
};

static ngx_http_module_t ngx_http_mydemo_handler_module_ctx ={
	NULL,									/* preconfiguraton */
	ngx_http_mydemo_ctx_postconfiguration,					/* postconfiguration */
	NULL,									/* create main configuration */
	NULL,									/* init main configuration */
	NULL,									/* create server configuration */
	NULL,									/* mersge server configuration */
	ngx_http_mydemo_create_loc_conf,		/* create location configuration */
	NULL									/* merge location configuration */
};

ngx_module_t ngx_http_mydemo_handler_module = {
	NGX_MODULE_V1,
	&ngx_http_mydemo_handler_module_ctx,
	ngx_http_mydemo_commands,
	NGX_HTTP_MODULE,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NGX_MODULE_V1_PADDING
};

//context configuration
// set actual  handler to chain
static ngx_int_t ngx_http_mydemo_ctx_postconfiguration(ngx_conf_t *pngx_conf){

	ngx_http_handler_pt   *h;
	ngx_http_core_main_conf_t * cmcf;
	cmcf = ngx_http_conf_get_module_main_conf(pngx_conf, ngx_http_core_module);
	
	h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
	if (h == NULL){
		return NGX_ERROR;
	}
	*h = ngx_http_mydemo_handler;
   return NGX_OK;		
}

static void * ngx_http_mydemo_create_loc_conf(ngx_conf_t *cf){
		ngx_http_hello_loc_conf_t * local_conf = NULL;
		local_conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t));
		if (local_conf == NULL){
				return NULL;
		}

		ngx_str_null(&local_conf->hello_string);
		local_conf->hello_counter = NGX_CONF_UNSET;

		return local_conf;
}

// command handler
static char* ngx_http_hello_string_command_handler(ngx_conf_t *pngx_conf, ngx_command_t *pngx_cmd, void* pconf){
	ngx_http_hello_loc_conf_t * local_conf;
	local_conf = pconf;
	char* rv = ngx_conf_set_str_slot(pngx_conf, pngx_cmd, local_conf);	
	ngx_conf_log_error(NGX_LOG_EMERG, pngx_conf, 0, "Hello_string:%s", local_conf->hello_string.data);
	return rv; 
}

static char* ngx_http_hello_counter_command_handler(ngx_conf_t *pngx_conf, ngx_command_t *pngx_cmd, void* pconf){
	ngx_http_hello_loc_conf_t * local_conf;
	local_conf = pconf;
	char * rv = NULL;
	rv = ngx_conf_set_flag_slot(pngx_conf, pngx_cmd, pconf);
	ngx_conf_log_error(NGX_LOG_EMERG, pngx_conf, 0, "Hello_counter:%d", local_conf->hello_counter);
	return rv;
}

// actual handler
static ngx_int_t ngx_http_mydemo_handler(ngx_http_request_t * request){

	static ngx_uint_t  ngx_hello_visited_times = 0;

	ngx_http_hello_loc_conf_t *my_conf;
	u_char ngx_hello_string[1024]={0};
	ngx_int_t rc;
	ngx_chain_t  out_chain;
	ngx_uint_t content_length = 0;
	ngx_buf_t *response_buffer; 

	// method
	ngx_log_error(NGX_LOG_EMERG,request->connection->log, 0, "ngx_http_hello_handler is called !");
	
	my_conf = (ngx_http_hello_loc_conf_t*)ngx_http_get_module_loc_conf(request, ngx_http_mydemo_handler_module);
	if (my_conf->hello_string.len == 0){
		ngx_log_error(NGX_LOG_EMERG, request->connection->log, 0, "hello_string is empty!");
		return NGX_DECLINED;
	}

	if (my_conf->hello_counter == NGX_CONF_UNSET 
		|| my_conf->hello_counter == 0){
		ngx_sprintf(ngx_hello_string, "%s", my_conf->hello_string.data);
	}else{
		ngx_sprintf(ngx_hello_string, "%s Visited Times:%d", my_conf->hello_string.data, ++ngx_hello_visited_times );
	}
	ngx_log_error(NGX_LOG_EMERG, request->connection->log, 0, "hello_string:%s", ngx_hello_string);
	content_length = ngx_strlen(ngx_hello_string);


	// response to 'GET'  'HEAD' only
	if(!(request->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))){
		return NGX_HTTP_NOT_ALLOWED;
	}

	// discard request body 
	rc = ngx_http_discard_request_body(request);
	if (rc != NGX_OK){
		return rc;
	}

     // make response header
 	 ngx_str_set(&request->headers_out.content_type, "text/html"); 
	 // if is HEAD
	 if ( request->method == NGX_HTTP_HEAD){
		request->headers_out.status = NGX_HTTP_OK;
		request->headers_out.content_length_n = content_length;

		return ngx_http_send_header(request);
	 }

	// allocate a buffer for response body
	response_buffer = ngx_pcalloc(request->pool, sizeof(ngx_buf_t));	  
	if (response_buffer == NULL){
		ngx_log_error(NGX_LOG_EMERG, request->connection->log, 0, "ngx_pcalloc return null , at 170!");
		return NGX_HTTP_INTERNAL_SERVER_ERROR;
	}

	out_chain.buf = response_buffer;
	out_chain.next = NULL;

	response_buffer->pos = ngx_hello_string;
	response_buffer->last = ngx_hello_string + content_length;
	response_buffer->memory = 1;
	response_buffer->last_buf = 1;
	
	request->headers_out.status = NGX_HTTP_OK;
	request->headers_out.content_length_n = content_length;

	rc = ngx_http_send_header(request);
	if (rc == NGX_ERROR ||  rc > NGX_OK || request->header_only){
		return rc;
	}
	
	 return ngx_http_output_filter(request, &out_chain);
	// return NGX_DECLINED;

}

